package br.ufc.great.xml;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

/**
 * 
 * @author Leonardo Oliveira Moreira
 * 
 * Classe que representa um documento XML
 */
public class XMLDocument implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1106819297256146956L;

	private XMLNode root;
	private long contId = -1;
	private Hashtable<String, XMLElement> index;
	private String name = null;
	
	public XMLDocument() {
		index = new Hashtable<String, XMLElement>();
	}

	/**
	 * Construtor que recebe como parâmetro um documento no formato JDOM, o nome do documento e transforma para uma representação própria
	 * 
	 * @param document
	 * @param name
	 */
	public XMLDocument(Document document, String name) {
		this();
		this.name = name;
		contId++;
		root = new XMLNode(contId, null);
		index.put(String.valueOf(contId), root);
		Element element = document.getRootElement();
		root.setName(element.getName());
		if (element.getChildren().size() == 0 && element.getValue().trim().length() > 0)
			root.setValue(element.getValue());

		List<Attribute> listAttr = element.getAttributes();
		Vector<XMLAttribute> attributes = new Vector<XMLAttribute>();
		for (int i = 0; i < listAttr.size(); i++) {
			Attribute attribute = (Attribute) listAttr.get(i);
			contId++;
			XMLAttribute xmlAttribute = new XMLAttribute(contId, attribute.getName(), attribute.getValue(), root);
			index.put(String.valueOf(contId), xmlAttribute);
			attributes.add(xmlAttribute);
		}
		if (attributes.size() > 0)
			root.setAttributes(attributes);
		List<Element> listChild = element.getChildren();
		for (int i = 0; i < listChild.size(); i++)
			addNode(root, ((Element) listChild.get(i)));
	}
	
	/**
	 * Método utilizado para adicionar um nodo em um elemento do documento XML
	 * 
	 * @param parent
	 * @param element
	 */
	private void addNode(XMLNode parent, Element element) {
		contId++;
		XMLNode node = new XMLNode(contId, parent);
		index.put(String.valueOf(contId), node);
		node.setName(element.getName());
		parent.addChildren(node);
		if (element.getChildren().size() == 0 && element.getValue().trim().length() > 0)
			node.setValue(element.getValue());

		List<Attribute> listAttr = element.getAttributes();
		Vector<XMLAttribute> attributes = new Vector<XMLAttribute>();
		for (int i = 0; i < listAttr.size(); i++) {
			Attribute attribute = (Attribute) listAttr.get(i);
			contId++;
			XMLAttribute xmlAttribute = new XMLAttribute(contId, attribute.getName(), attribute.getValue(), node);
			index.put(String.valueOf(contId), xmlAttribute);
			attributes.add(xmlAttribute);
		}
		if (attributes.size() > 0)
			node.setAttributes(attributes);
		List<Element> listChild = element.getChildren();
		for (int i = 0; i < listChild.size(); i++)
			addNode(node, ((Element) listChild.get(i)));
	}

	/**
	 * Método utilizado para resgatar o elemento raiz do documento XML
	 * 
	 * @return
	 */
	public XMLNode getRoot() {
		return root;
	}
	
	/**
	 * Método utilizado para configurar o elemento raiz do documento XML
	 * 
	 * @param root
	 */
	public void setRoot(XMLNode root) {
		this.root = root;
	}
	
	/**
	 * Método utilizado para transformar o documento XML no formato de String
	 */
	public String toString() {
		String strDocument = "";
		strDocument = printElement(root, 1);
		return strDocument;
	}

	/**
	 * Método utilizado para imprimir um elemento do documento XML
	 * 
	 * @param current
	 * @param level
	 * @return
	 */
	private String printElement(XMLNode current, int level) {
		String temp = "";
		for (int i = 0; level != 1 && i < (level * 2); i++)
			temp += " ";
		temp += "<" + current.getName();
		String strAttributes = "";
		Vector<XMLAttribute> attributes = current.getAttributes();
		for (int i = 0; attributes != null && i < attributes.size(); i++){
			XMLAttribute attribute = attributes.elementAt(i);
			strAttributes += " " + attribute.getName() + "=\"" + attribute.getValue() + "\"";
		}
		temp += strAttributes;
		Vector<XMLNode> childrens = current.getChildren();
		if (childrens != null && childrens.size() > 0) {
			temp += ">\n";
			for (int i = 0; i < childrens.size(); i++) {
				XMLNode child = childrens.elementAt(i);
				temp += printElement(child, level + 1);
			}
			for (int i = 0; level != 1 && i < (level * 2); i++)
				temp += " ";
			temp += "</" + current.getName()+ ">\n";			
		}
		else
			if (current.getValue() != null && current.getValue().trim().length() > 0)
				temp += ">" + current.getValue().trim() + "</" + current.getName()+ ">\n";
			else
				temp += " />\n";
		return temp;
	}

	/**
	 * Método que retorna um novo id para um elemento do documento XML
	 * 
	 * @return
	 */
	public long getNewId() {
		return (++contId);
	}

	/**
	 * Método utilizado para retornar um elemento do documento XML pelo identificador
	 * 
	 * @param id
	 * @return
	 */
	public XMLElement getElementById(long id) {
		if (index.contains(String.valueOf(id)))
			return index.get(String.valueOf(id));
		return null;
	}
	
	/**
	 * Método que retorna o último id de um elemento do documento XML
	 * 
	 * @return
	 */
	public long getLastId() {
		return contId;
	}
	
	/**
	 * Método utilizado para retornar o nome do documento XML
	 * 
	 * @return
	 */
	public String getName() {
		return name;
	}
	
}
